package com.atguigu.spzx.common.log.aspects;

import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.exception.GuiguException;
import com.atguigu.spzx.common.log.annotations.Log;
import com.atguigu.spzx.common.log.service.SysOperLogService;
import com.atguigu.spzx.common.util.IpUtil;
import com.atguigu.spzx.common.util.LoginAuthUtil;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Date;

//@Component
@Slf4j
@Aspect //表示当前类为一个切面类
public class LogAspect {
    // common-log项目不会单独启动，在manager服务启动时，因为import了LogAspect
    // manager服务的spring容器会初始化LogAspect的对象，LogAscpect自动装配sysOperLogService时
    // 可以从manager的spring容器中获取到实现类对象
    @Resource
    SysOperLogService sysOperLogService;

    //使用环绕通知 对加Log注解的方法进行增强
//    @Around(value = "execution(* com.atguigu.spzx.*..*(..) )") //使用切面表达式对方法进行切面
    @Around(value = "@annotation(com.atguigu.spzx.common.log.annotations.Log)")
    public Object saveLog(ProceedingJoinPoint joinPoint){//joinPoint可以获取到目标方法的基本所有的配置
        //目标方法执行前的操作
        //准备工作： 准备保存日志需要的属性值:
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//目标方法签名对象
        //0.0 获取目标方法对象
        Method method = signature.getMethod();
        //- 0.1 获取目标方法上的注解对象
        Log annotation = method.getAnnotation(Log.class);
        //0.2 - 获取参数列表
        Object[] args = joinPoint.getArgs();//目标方法参数列表
        //1、初始化日志对象
        SysOperLog sysOperLog = new SysOperLog();
        try {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            //2、设置日志对象的属性值
            sysOperLog.setTitle(annotation.title());//访问的接口标题
            sysOperLog.setMethod(joinPoint.getTarget().getClass().getName());//访问的模块: 访问的controller的类名
            sysOperLog.setRequestMethod(request.getMethod());//请求方式
            sysOperLog.setOperatorType(annotation.operatorType().name());//操作类型
            sysOperLog.setBusinessType(annotation.businessType());// 业务类型
            sysOperLog.setOperName(LoginAuthUtil.get().getUserName());//操作管理员账号
            sysOperLog.setOperUrl(request.getRequestURI());//接口路径
            sysOperLog.setOperIp(IpUtil.getIpAddress(request));//客户端ip地址
            if(annotation.isSaveRequestParams()){
                sysOperLog.setOperParam(ArrayUtils.isEmpty(args)?null: StringUtils.join(args,",")); //请求参数
            }

            sysOperLog.setOperTime(new Date());
            sysOperLog.setCreateTime(new Date());
            sysOperLog.setUpdateTime(new Date());
            //3、执行目标方法
            Object result = joinPoint.proceed();
            //4、目标方法正常执行的操作：保存正常的日志
            sysOperLog.setJsonResult(JSON.toJSONString(result));//返回结果
            sysOperLog.setStatus(0);//业务执行状态  0正常
            return result; //返回目标方法执行的结果
        } catch (Throwable e) {
            //5、目标方法执行异常时的操作:保存异常的日志

            sysOperLog.setStatus(1);//业务执行状态  0正常
            sysOperLog.setErrorMsg(e.getMessage());//执行状态为1时 出现异常的异常信息


            if(e instanceof GuiguException){
                throw (GuiguException)e;
            }else{
                throw new RuntimeException(e);
            }
        }finally {
            //保存日志到数据库中
            sysOperLogService.saveSysOperLog(sysOperLog);
        }

    }

}
